
/**
 * 飞行距离和步行距离（空间距离-表面距离）
 * @class
 * MeasureDistance
 * @param{Cesium.Viewer} viewer
 *
 */
class MeasureDistance {
  constructor(viewer) {
    this.viewer = viewer;
    this.resultLayer = new Cesium.CustomDataSource("结果图层");
    this.drawLayer = new Cesium.CustomDataSource("绘制图层");
    this.viewer.dataSources.add(this.drawLayer);
    this.viewer.dataSources.add(this.resultLayer);
    this.handler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas);
    this.pointArr = [];
    this.tip;
    this.measureResult = [];
    this.goMesaure = true;
  }

  /**
   * @method start 开始测量
   * @param {number} precision  计算精度(主要用于表面距离的计算精度)
   */
  start(precision = 5) {
    //检测测量的机制
    if (!this.viewer.terrainProvider.availability) {
      ElMessage({
        type: "error",
        message: "不存在地形数据，无法测量！",
        duration: 3000,
        offset: 90,
      });
      return;
    }

    this.pointArr = [];
    let tempSpaceLine, tempSurfaceLine;
    this.tip = createFollowTip(this.viewer, "单击加点绘制，右击开始计算！");
    this.handler.setInputAction(async (e) => {
      let position = this.viewer.scene.pickPosition(e.position);
      if (!Cesium.defined(position)) return;
      let point = this.createPoint(position);
      this.drawLayer.entities.add(point);
      this.pointArr.push(position);
      if (this.pointArr.length >= 2) {
        let positions = [
          this.pointArr[this.pointArr.length - 2],
          this.pointArr[this.pointArr.length - 1],
        ];
        let Line1 = new Cesium.Entity({
          polyline: {
            positions: positions,

            material: new Cesium.PolylineGlowMaterialProperty({
              glowPower: 0.15,
              color: Cesium.Color.fromCssColorString("#37a1fb"),
            }),
            width: 7,
          },
        });
        let Line2 = new Cesium.Entity({
          polyline: {
            positions: positions,

            material: new Cesium.PolylineGlowMaterialProperty({
              glowPower: 0.15,
              color: Cesium.Color.fromCssColorString("#f9c532"),
            }),
            width: 7,
            clampToGround: true,
          },
        });
        this.drawLayer.entities.add(Line1);
        this.drawLayer.entities.add(Line2);
      }
    }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
    this.handler.setInputAction((mouse) => {
      let position = this.viewer.scene.pickPosition(mouse.endPosition);
      if (this.pointArr.length >= 1 && Cesium.defined(position)) {
        if (Cesium.defined(tempSpaceLine)) {
          this.drawLayer.entities.remove(tempSpaceLine);
        }
        if (Cesium.defined(tempSurfaceLine)) {
          this.drawLayer.entities.remove(tempSurfaceLine);
        }
        let line1Positions = [
          this.pointArr[this.pointArr.length - 1],
          position,
        ];
        tempSpaceLine = new Cesium.Entity({
          polyline: {
            positions: new Cesium.CallbackProperty(() => {
              return line1Positions;
            }, false),

            material: new Cesium.PolylineGlowMaterialProperty({
              glowPower: 0.15,
              color: Cesium.Color.fromCssColorString("#37a1fb"),
            }),
            width: 7,
          },
        });
        tempSurfaceLine = new Cesium.Entity({
          polyline: {
            positions: new Cesium.CallbackProperty(() => {
              return line1Positions;
            }, false),

            material: new Cesium.PolylineGlowMaterialProperty({
              glowPower: 0.15,
              color: Cesium.Color.fromCssColorString("#f9c532"),
            }),
            width: 7,
            clampToGround: true,
          },
        });
        this.drawLayer.entities.add(tempSpaceLine);
        this.drawLayer.entities.add(tempSurfaceLine);
      }
    }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
    this.handler.setInputAction(() => {
      this.drawLayer.entities.remove(tempSpaceLine);
      this.drawLayer.entities.remove(tempSurfaceLine);
      this.resultLayer.entities.add(
        new Cesium.Entity({
          polyline: {
            positions: this.pointArr,
            material: new Cesium.PolylineGlowMaterialProperty({
              glowPower: 0.15,
              color: Cesium.Color.fromCssColorString("#37a1fb"),
            }),
            width: 7,
          },
        })
      );
      this.resultLayer.entities.add(
        new Cesium.Entity({
          polyline: {
            positions: this.pointArr,
            material: new Cesium.PolylineGlowMaterialProperty({
              glowPower: 0.15,
              color: Cesium.Color.fromCssColorString("#f9c532"),
            }),
            width: 7,
            clampToGround: true,
          },
        })
      );
      this.tip.change("正在计算...");
      this.removeEvent();
      if (this.pointArr.length >= 2) {
        this.calSumDistance(this.pointArr, precision);
      }
      this.tip.close();
      if (this.goMesaure) {
        this.start();
      }
    }, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
  }

  /**
   * @method calSumDistance 计算总和距离
   * @param {array} pointArr 要计算的数组
   */
  async calSumDistance(pointArr, precision) {
    for (let i = 0; i <= pointArr.length - 2; i++) {
      let positions = [pointArr[i + 1], pointArr[i]];

      let tempPos = pointArr[i + 1];
      let tempCart = Cesium.Cartographic.fromCartesian(tempPos);
      tempPos = Cesium.Cartesian3.fromRadians(
        tempCart.longitude,
        tempCart.latitude,
        tempCart.height + 10
      );
      let label = new Cesium.Entity({
        position: tempPos,
        label: {
          text: "正在计算...",
          fillColor: Cesium.Color.YELLOW,
          // heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
          pixelOffset: new Cesium.Cartesian2(0, -20),
          font: "bold 17px KaiTi ",
          // eyeOffset: new Cesium.Cartesian3(0, 0, -1000)
          disableDepthTestDistance: 30000,
          scaleByDistance: new Cesium.NearFarScalar(1, 1.5, 300000, 0.3),
          heightReference: Cesium.HeightReference.NONE,
        },
      });
      this.resultLayer.entities.add(label);

      console.log(label);

      let result = await this.calDistance(...positions, precision);
      label.label.text._value = `飞行距离:${result.spaceDistance}\n步行距离${result.surfaceDistance}`;
    }
  }

  /**
   * @method removeEvent 事件移除器
   *
   */
  removeEvent() {
    this.handler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK);
    this.handler.removeInputAction(Cesium.ScreenSpaceEventType.MOUSE_MOVE);
    this.handler.removeInputAction(Cesium.ScreenSpaceEventType.RIGHT_CLICK);
    if (this.tip) {
      this.tip.close();
    }
  }

  //继续测量
  /**
   * @method changeMeasure 改变继续测量
   */
  changeMeasure() {
    this.goMesaure = true;
    this.start();
  }

  /**
   * @method clear 清空测量
   */
  clear() {
    this.tip && this.tip.close()
    this.removeEvent();
    this.drawLayer.entities.removeAll();
    this.resultLayer.entities.removeAll();
    this.goMesaure = false;
    this.pointArr = [];
  }

  /**
   * @method formatDistance 规范化距离
   * @param {number} distance 距离
   * @returns result
   */
  formatDistance(distance) {
    return (distance / 1000).toFixed(3) + "公里";
  }

  /**
   * @method calDistance 计算距离
   * @param {Cartesian}  start  起点
   * @param {Cartesian} end  终点
   */

  async calDistance(start, end, precision) {
    return new Promise((resolve, reject) => {
      let obj = {
        spaceDistance: null,
        surfaceDistance: null,
      };
      //计算直线距离
      obj.spaceDistance = this.formatDistance(
        Cesium.Cartesian3.distance(start, end)
      );

      //根据量测的单位长度优化精度
      if (Cesium.Cartesian3.distance(start, end) / 1000 / 10 >= 1) {
        //>10公里
        precision = 0.5;
      } else if (Cesium.Cartesian3.distance(start, end) / 1000 / 10 >= 0.5) {
        //  5-10公里
        precision = 1;
      } else {
        precision = 10;
      }

      //[计算表面距离
      //获取插值点
      let tranches =
        Cesium.Cartesian3.distance(start, end).toFixed(0) * precision;
      let lerpPointArr = [start];
      for (let i = 1; i < tranches; i++) {
        let cartesian = new Cesium.Cartesian3();
        Cesium.Cartesian3.lerp(start, end, i / tranches, cartesian);
        lerpPointArr.push(cartesian);
      }
      lerpPointArr.push(end);

      // //#region 展示平面插值点
      // lerpPointArr.forEach((e) => {
      //   let lerpPoint = new Cesium.Entity({
      //     position: e,
      //     point: {
      //       pixelSize: 5,
      //       color: Cesium.Color.BLUE,
      //     },
      //   });
      //   this.drawLayer.entities.add(lerpPoint);
      // });
      // //#endregion 展示平面插值点

      //开始对点位进行采样

      (async () => {
        lerpPointArr = lerpPointArr.map((e) => {
          return Cesium.Cartographic.fromCartesian(e);
        });
        let updateHeightPositions = await Cesium.sampleTerrainMostDetailed(
          this.viewer.terrainProvider,
          lerpPointArr
        );
        let surfaceDistance = 0;
        // // #region 展示添加点位：（可注释）
        // //#region -- 展示表面插值点
        // updateHeightPositions.forEach((item) => {
        //   let point = new Cesium.Entity({
        //     position: Cesium.Cartesian3.fromRadians(
        //       item.longitude,
        //       item.latitude,
        //       item.height - 0.5
        //     ),
        //     point: {
        //       pixelSize: 5,
        //       color: Cesium.Color.RED,
        //     },
        //   });
        //   this.drawLayer.entities.add(point);
        // });
        // //#endregion -- 展示表面插值点
        // //#endregion展示添加点位：（可注释）
        for (let j = 0; j < updateHeightPositions.length - 2; j++) {
          let pointForward = Cesium.Cartesian3.fromRadians(
            updateHeightPositions[j].longitude,
            updateHeightPositions[j].latitude,
            updateHeightPositions[j].height - 0.5
          );
          let pointBackward = Cesium.Cartesian3.fromRadians(
            updateHeightPositions[j + 1].longitude,
            updateHeightPositions[j + 1].latitude,
            updateHeightPositions[j + 1].height - 0.5
          );
          surfaceDistance += Cesium.Cartesian3.distance(
            pointForward,
            pointBackward
          );
        }
        obj.surfaceDistance = this.formatDistance(surfaceDistance);
        resolve(obj);
        // console.log(updateHeightPositions, "重采样后点位");
        // console.log(obj, "计算结果");
      })();

      //   console.log(tranches, "份额");
    });
  }

  //#region 工具函数
  createPoint(cartesian) {
    let point = new Cesium.Entity({
      position: cartesian,
      point: {
        pixelSize: 7,
        color: Cesium.Color.YELLOW,
        outline: true,
        outlineWidth: 2,
        outlineColor: Cesium.Color.AQUA,
      },
    });
    return point;
  }

  //#endregion
}

export default MeasureDistance;

function createFollowTip(viewer, text) {
  let div = document.createElement("div");
  let body = document.querySelector("body");
  div.style.position = "absolute";
  div.style.top = "0px";
  div.style.left = "0px";
  div.style.display = "none";
  div.style.minWidth = "100px";
  div.style.minHeight = "20px";
  div.style.padding = "2px 10px";
  div.style.boxSizing = "border-box";
  div.style.pointerEvents = "none";
  div.style.backgroundColor = "#27323850";
  div.style.zIndex = "3000";
  div.style.borderRadius = "15px";
  div.style.color = "white";
  div.style.font = "bold 16px KaiTi";
  div.style.textAlign = "center";
  div.style.lineHeight = "20px";
  div.innerText = text;

  div.close = () => {
    div.style.display = "none";
    body.removeEventListener("mousemove", updateMouseMove);
    _handler.removeInputAction(Cesium.ScreenSpaceEventType.MOUSE_MOVE);
    console.log(div.parentElement, "父元素");
    if (div.parentElement) {
      div.parentElement.removeChild(div);
    }
    // body.removeChild(div);
  };

  div.change = (newText) => {
    div.innerText = newText;
  };

  body.appendChild(div);
  body.style.overflow = "hidden";

  function updatePosition(cartesain3) {
    let position = Cesium.SceneTransforms.wgs84ToWindowCoordinates(
      viewer.scene,
      cartesain3
    );
    let { x, y } = position;
    div.style.transform = `translate(${x}px, calc(${y}px - 100%)`;
  }

  let _handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
  _handler.setInputAction((e) => {
    viewer.scene.globe.depthTestAgainstTerrain = true;
    let cartesain3 = viewer.scene.pickPosition(e.endPosition);
    if (Cesium.defined(cartesain3)) {
      viewer.scene.postRender.addEventListener(updatePosition(cartesain3));
    } else {
      div.style.display = "none";
      viewer.scene.postRender.removeEventListener(updatePosition);
    }
  }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);

  let updateMouseMove = (e) => {
    div.style.display = "block";
    let x = e.clientX;
    let y = e.clientY;
    div.style.transform = `translate(${x + 2}px, calc(${y - 2}px - 100%)`;
  };

  body.addEventListener("mousemove", updateMouseMove);
  return div;
}
